package com.zs.backend.common.utils;

import com.alibaba.fastjson.JSON;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.beanutils.PropertyUtils;


@Slf4j
public class CollectionUtil {

    /**
     * 去掉重复项
     *
     * @param objList
     */
    public static List<Object> repeatList(List<String> objList) {
        List<Object> list = null;

        if (objList != null && objList.size() > 0) {
            Set<Object> set = new HashSet<>(objList);
            list = new ArrayList<>();
            list.clear();
            list.addAll(set);
        }

        return list;
    }

    public static boolean isNullOrEmpty(Collection<?> collection) {
        return collection == null || collection.isEmpty();
    }

    public static boolean isNotNullAndEmpty(Collection<?> collection) {
        return !isNullOrEmpty(collection);
    }

    public static boolean isNullOrEmpty(Map<?, ?> map) {
        return map == null || map.isEmpty();
    }

    public static boolean isNotNullAndEmpty(Map<?, ?> map) {
        return !isNullOrEmpty(map);
    }

    /**
     * 从给定的数组中选取k个元素
     * @param index 选取开始位置索引
     * @param k 选取元素个数
     * @param origin 原始数组
     * @param tmpList 临时list(调用时传空list)
     * @param resultList 所有组合结果
     * @param <T>
     */
    public static <T> void combination(int index,int k,List<T> origin,List<T> tmpList,List<List<T>> resultList) {
        if(origin==null ||tmpList==null ||resultList==null){
            return;
        }
        if(k == 1){
            for (int i = index; i < origin.size(); i++) {
                tmpList.add(origin.get(i));
                List<T> temp = new ArrayList<>(tmpList.size());
                temp.addAll(tmpList);
                resultList.add(temp);
                tmpList.remove(origin.get(i));
            }
        }else if(k > 1){
            for (int i = index; i <= origin.size() - k; i++) {
                //tmpArr都是临时性存储一下
                tmpList.add(origin.get(i));
                //索引右移，内部循环，自然排除已经选择的元素
                combination(i + 1,k - 1, origin,tmpList,resultList);
                //tmpArr因为是临时存储的，上一个组合找出后就该释放空间，存储下一个元素继续拼接组合了
                tmpList.remove(origin.get(i));
            }
        }
    }

    /**
     * 按找指定顺序依次匹配,并返回首个匹配结果，未匹配返回空列表
     *
     * @param srcList      待匹配规则列表
     * @param reduceOrder  减条件的顺序（ 不能精确匹配时，用于减条件再次匹配，一次减多个条件用,分隔)
     * @param condition    包含匹配条件的对象
     * @param keyFieldList 匹配条件拼接的key
     * @param <T>
     * @return 匹配到的策略
     */
    public static <T> List<T> matchStrategy(List<T> srcList, List<Set<String>> reduceOrder, Object condition, List<String> keyFieldList) {
        if (CollectionUtil.isNullOrEmpty(srcList) || condition == null || CollectionUtil.isNullOrEmpty(keyFieldList)) {
            return Collections.emptyList();
        }
        log.debug("开始匹配策略reduceOrder:{},condition:{} keyFieldList:{}", reduceOrder, condition, keyFieldList);
        Map<String, List<T>> collect = srcList.stream().collect(Collectors.groupingBy(s -> getKey(s, keyFieldList,Collections.emptySet())));
        String allKey = getKey(condition, keyFieldList,Collections.emptySet());
        List<T> matchList = null;
        if (collect.containsKey(allKey)) {
            matchList = collect.get(allKey);
            log.debug("精确匹配策略 key:{} ", allKey, matchList);
        } else {
            log.debug("未精确匹配策略 开始减项匹配");
            for (Set<String> reduce : reduceOrder) {
                if (CollectionUtil.isNullOrEmpty(reduce)) {
                    log.debug("未配置减项匹配规则");
                    continue;
                }
                log.debug("匹配策略 减项为:{}", reduce);
                List<T> ts = matchStrategy(collect, condition, keyFieldList, reduce);
                if (CollectionUtil.isNotNullAndEmpty(ts)) {
                    return ts;
                }
            }
        }
        return matchList==null?Collections.emptyList():matchList;
    }
    /**
     * 按找指定顺序依次匹配,并返回首个匹配结果，未匹配返回空列表
     *
     * @param collect      待匹配规则map
     * @param condition    包含匹配条件的对象
     * @param keyFieldList 匹配条件拼接的key
     * @param reduceFields  减去的条件
     * @param <T>
     * @return 匹配到的策略
     */
    private static <T> List<T> matchStrategy(Map<String, List<T>> collect, Object condition, List<String> keyFieldList,Set<String> reduceFields) {
        String key = getKey(condition, keyFieldList, reduceFields);
        List<T> object = collect.get(key);
        if (log.isDebugEnabled()&&collect.containsKey(key)) {
            log.debug("匹配策略减项匹配key:{} 匹配成功 匹配策略:{}", key, JSON.toJSONString(object));
        }
        return object;
    }

    public static String getKey(Object src, List<String> keyField){
        return getKey(src,keyField,null);
    }

    public static String getKey(Object src, List<String> keyField, final Set<String> reduceFields) {
        if (src == null || CollectionUtil.isNullOrEmpty(keyField)) {
            return null;
        }
        String fmt = "%s=%s";
        return keyField.stream().map(k -> {
            Object val = null;
            if (reduceFields==null||!reduceFields.contains(k)) {
                try {
                    val = PropertyUtils.getProperty(src, k);
                } catch (Exception e) {
                }
            }
            return String.format(fmt, k, val == null ? "" : val.toString());
        }).collect(Collectors.joining(","));
    }

    public static <T> List<T> distinct(List<T> list,Function<? super T, ?> keyExtractor){
        if(isNullOrEmpty(list)||keyExtractor==null){
            return list;
        }
        return list.stream().filter(distinctByKey(keyExtractor::apply)).collect(Collectors.toList());
    }

    public static <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) {
        Set<Object> seen = ConcurrentHashMap.newKeySet();
        return t -> seen.add(keyExtractor.apply(t));
    }
}